#region Unit Testing
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RacingGame.Graphics;
using RacingGame.Helpers;
using System;
using System.Collections.Generic;
using System.Text;
using RacingGame.Shaders;
#endregion

namespace RacingGame.Graphics
{
    public class LineManager2D : IDisposable
    {
        #region Variables
        private int numOfLines = 0;

        private List<Line> lines = new List<Line>();

        struct Line
        {
            #region Variables
            public Point startPoint, endPoint;

            public Color color;
            #endregion

            #region Constructor
            public Line(Point setStartPoint,
                Point setEndPoint, Color setColor)
            {
                startPoint = setStartPoint;
                endPoint = setEndPoint;
                color = setColor;
            }
            #endregion

            #region Equals operators
            public static bool operator ==(Line a, Line b)
            {
                return a.startPoint == b.startPoint &&
                    a.endPoint == b.endPoint &&
                    a.color == b.color;
            }

            public static bool operator !=(Line a, Line b)
            {
                return a.startPoint != b.startPoint ||
                    a.endPoint != b.endPoint ||
                    a.color != b.color;
            }

            public override bool Equals(object a)
            {
                if (a is Line)
                    return (Line)a == this;

                return false;
            }

            public override int GetHashCode()
            {
                return 0;
            }
            #endregion
        }

        private bool buildVertexBuffer = false;

        VertexPositionColor[] lineVertices =
            new VertexPositionColor[MaxNumOfLines * 2];

        private int numOfPrimitives = 0;

        private const int MaxNumOfLines = 64;

        VertexDeclaration decl = null;
        #endregion

        #region Constructor
        public LineManager2D()
        {
            decl = new VertexDeclaration(
                RacingGameManager.Device, VertexPositionColor.VertexElements
                );
        }
        #endregion

        #region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                decl.Dispose();
            }
        }
        #endregion

        #region Vertex buffer stuff
        private void UpdateVertexBuffer()
        {
            if (numOfLines == 0 ||
                lines.Count < numOfLines)
            {
                numOfPrimitives = 0;
                return;
            }
#if LOG_STUFF
            Log.Write("LineManager.UpdateVertexBuffer() numOfLines=" +
                numOfLines + ", buildVertexBuffer=" + buildVertexBuffer);
#endif

            for (int lineNum = 0; lineNum < numOfLines; lineNum++)
            {
                Line line = (Line)lines[lineNum];
                lineVertices[lineNum * 2 + 0] = new VertexPositionColor(
                    new Vertex3(
                        -1.0f + 2.0f * line.startPoint.X / BaseGame.Width,
                        -(-1.0f + 2.0f * line.startPoint.Y / BaseGame.Height), 0),
                        line.color);
                lineVertices[lineNum * 2 + 1] = new VertexPositionColor(
                    new Vertex3(
                        -1.0f + 2.0f * line.endPoint.X / BaseGame.Width,
                        -(-1.0f + 2.0f * line.endPoint.Y / BaseGame.Height), 0),
                        line.color);
            }

            numOfPrimitives = numOfLines;

            buildVertexBuffer = false;
        }
        #endregion

        #region Add line
        public void AddLine(Point startPoint, Point endPoint, Color color)
        {
            if (numOfLines >= MaxNumOfLines)
            {
                Log.Write("Too many lines requested in LineManager2D. " + 
                    "Max lines = " + MaxNumOfLines);
                return;
            }

            Line line = new Line(startPoint, endPoint, color);

            if (lines.Count > numOfLines)
            {
                if ((Line)lines[numOfLines] != line)
                {
                    lines[numOfLines] = line;
                    buildVertexBuffer = true;
                }
            }
            else
            {
                lines.Add(line);
                buildVertexBuffer = true;
            }

            numOfLines++;
        }

        public void AddLineWithShadow(Point startPoint, Point endPoint,
                Color color)
        {
            AddLine(new Point(startPoint.X, startPoint.Y + 1),
                new Point(endPoint.X, endPoint.Y + 1), Color.Black);
            AddLine(startPoint, endPoint, color)
        }
        #endregion

        #region Render
        public virtual void Render()
        {
            if (buildVertexBuffer ||
                    numOfPrimitives != numOfLines)
            {
                UpdateVertexBuffer();
            }

            if (numOfPrimitives > 0)
            {
                BaseGame.SetAlphaBlendingEnabled(true);
                BaseGame.WorldMatrix = Matrix.Identity;
                ShaderEffect.lineRendering.Render(
                    "LineRendering2D",
                    delegate
                    {
                        BaseGame.Device.VertexDeclaration = decl;
                        BaseGame.Device.DrawUserPrimitives<VertexPositionColor>(
                            PrimitiveType.LineList, lineVertices, 0, numOfPrimitives);
                    }
                    );
            }

            numOfLines = 0;
        }
        #endregion
    }
}
